#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
#include <linux/kthread.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/rcupdate.h>
#include <linux/kthread.h>
struct foo {
	int a;
	struct rcu_head rcu;
};

struct foo *g_ptr;

static int myrcu_reader_thread(void *data)
{
	struct foo *p = NULL;
	while (!kthread_should_stop()) {
		msleep(500);
		rcu_read_lock();
		p = rcu_dereference(g_ptr);
		if (p)
			pr_info("%s : read a = %d\r\n", __func__, p->a);
		rcu_read_unlock();

		// if (p->a > 20)
		// 	break;
	}

	pr_info("exit %s\r\n", __func__);

	return 0;
}

static void myrcu_del(struct rcu_head *rh)
{
	struct foo *p = container_of(rh, struct foo, rcu);
	pr_info("%s: a = %d\n", __func__, p->a);
	kfree(p);
}

static int myrcu_writer_thread(void *p)
{
	struct foo *new;
	struct foo *old;
	int value = *(int *)p;

	while (!kthread_should_stop()) {
		msleep(1000);
		new = kmalloc(sizeof(struct foo), GFP_KERNEL);
		old = g_ptr;
		pr_info("%s: write to new %d\n", __func__, value);
		*new = *old;
		new->a = value;
		rcu_assign_pointer(g_ptr, new);
		call_rcu(&old->rcu, myrcu_del);
		value++;

		if (value > 100)
			break;
	}

	pr_info("exit %s value %d\r\n", __func__, value);

	return 0;
}

struct task_struct *reader_thread;
struct task_struct *writer_thread;

static int __init myrcu_init(void)
{
	int value = 0;

	g_ptr = kmalloc(sizeof(struct foo), GFP_KERNEL);
	g_ptr->a = value;

	reader_thread = kthread_run(myrcu_reader_thread, NULL, "rcu_reader");
	writer_thread =
		kthread_run(myrcu_writer_thread, (void *)&value, "rcu_writer");

	return 0;
}

static void myrcu_exit(void)
{
	kthread_stop(reader_thread);
	kthread_stop(writer_thread);

	rcu_barrier();
	kfree(g_ptr);
}

module_init(myrcu_init);
module_exit(myrcu_exit);

MODULE_DESCRIPTION("Module named myrcu.");
MODULE_LICENSE("GPL");
